#pragma once

#include "World Includes.h"
#include "../Global References/VectorContainer.h"
#include "_generator definitions.h"
#include <time.h>

#include "Chunk.h"

class _Generator {
private:
	////Holds the entire map
	_Chunk ** pMap;

	////A space of memory for the chunk that is being generated to use
	Block * Buffer;
	
	////A list of the Borders of all chunks that have been generated
	CHUNK_INFO * Generated;

	////The current chunk that is being generated
	_Chunk * current;

	//The x and y position of the current chunk/block being modified in the world
	short ccx,ccy,cbx,cby;
	//	ccX = current chunk X , cbx = current block X
	//	ccY = current chunk Y , cby = current block Y

	////Extra info
	//// How many chunks wide and tall the world is
	int MapH, MapW;

	////TOOLS
	//// A list of all the tools available to the generator
	Tool ToolList[E_TOOL_SIZE];
	//// A parralel list that holds whether the tool has been used yet or not
	bool Tools[E_TOOL_SIZE];

	////A list of all the biomes in the world in order from left to right
	Vector<PBIOME> BiomeList;
	////The current biome that the generator is getting its rules form
	BIOME * CurrentBiome;
	////The index of the current biome
	int BiomeIndex;
	
	_Generator() : pMap(NULL) ,
		//GeneratedMap(NULL) ,
		Buffer(NULL) ,
		current(NULL) ,
		ccx(0) , ccy(0) , cbx(0) , cby(0) ,
		MapH(0),MapW(0) ,
		CurrentBiome(NULL) ,
		BiomeIndex(0)
	{
		for(int i = E_TOOL_SIZE-1;i >= 0;--i)
			ToolList[i] = NULL;
	}
public:
	inline static _Generator * Ins() {
		static _Generator G;
		return & G;
	}
	~_Generator() {
		CleanUp();
		ShutDown();
	}

	void Initialize(_Chunk ** a_pMap,int a_MapH,int a_MapW) {
		pMap = a_pMap;
		MapH = a_MapH;
		MapW = a_MapW;
		Generated = new CHUNK_INFO[MapW * MapH];
		for(int i = 0;i < MapW * MapH;i++) {
			Generated[i].Setup();
		}
		Buffer = new Block[CHUNK_HEIGHT * CHUNK_WIDTH];
		current = pMap[0];
		ccx = cbx = 0;
		ccy = cby = 0;
		SetupTools();
		SetupBiomes();
	}

	void ReInit() {
		Generated = new CHUNK_INFO[MapW * MapH];
		for(int i = 0;i < MapW * MapH;i++) {
			Generated[i].Setup();
		}
		current = pMap[0];
		ccx = cbx = 0;
		ccy = cby = 0;
		SetBiomes();
	}

	void SetupTools() {
		for(int i = E_TOOL_SIZE-1;i >= 0;i--) {
			ToolList[i] = NULL;
			Tools[i] = false;
		}
		ToolList[E_TOOL_RANDOM] = &_Generator::RandomizeGroundLevel;
		Tools[E_TOOL_RANDOM] = true;
		ToolList[E_TOOL_CAVE] = &_Generator::CaveWalk;
		Tools[E_TOOL_CAVE] = true;
		ToolList[E_TOOL_GRASS] = &_Generator::ApplyGrass;
		Tools[E_TOOL_GRASS] = true;
		ToolList[E_TOOL_STONE] = &_Generator::ApplyStone;
		Tools[E_TOOL_STONE] = true;
		ToolList[E_TOOL_TREES] = &_Generator::PlaceTrees;
		Tools[E_TOOL_TREES] = true;
		ToolList[E_TOOL_ORE1] = &_Generator::PlaceOre1;
		Tools[E_TOOL_ORE1] = true;
	}

	void SetupBiomes() {
		PBIOME ToAdd;
		for(int i = 0;i < BIOME_SIZE;i++) {
			ToAdd = new _Biome();

			ToAdd->CreateBiome(G_BiomePathList[i]);

			BiomeList.push(ToAdd);
		}
		SetBiomes();
	}

	void SetBiomes() {
		//The biomeindex for any given biome
		int ColBiome = 0;
		for(int x = MapW-1;x >= 0;x--) {
			//0 = plains
			//1 = cave
			//2 = mountain
			//3 = forest
			ColBiome = rand() % BIOME_SIZE;

			for(int y = MapH-1;y >= 0;y--) {
				(*pMap)[x + (y * MapH)].SetBiome(ColBiome);
			}
		}
	}

	void DoubleCheckValues() {
		if(ccx < 0) ccx = 0;
		if(ccx >= MapW) ccx = MapW-1;
		if(ccy < 0) ccy = 0;
		if(ccy >= MapH) ccy = MapH-1;

		if(cbx < 0) {
			while(cbx < 0) {
				cbx+=CHUNK_WIDTH;
				ccx--;
				if(ccx < 0) {
					ccx = 0;
					cbx = 0;
					break;
				}
			}
		}
		else if (cbx >= CHUNK_WIDTH) {
			while(cbx >= CHUNK_WIDTH) {
				cbx-=CHUNK_WIDTH;
				ccx++;
				if(ccx >= MapW) {
					ccx = MapW-1;
					cbx = CHUNK_WIDTH-1;
					break;
				}
			}
		}


		if(cby < 0) {
			while(cby < 0) {
				cby+=CHUNK_HEIGHT;
				ccy--;
				if(ccy < 0) {
					ccy = 0;
					cby = 0;
					break;
				}
			}
		}
		else if (cby >= CHUNK_HEIGHT) {
			while(cby >= CHUNK_HEIGHT) {
				cby-=CHUNK_HEIGHT;
				ccy++;
				if(ccy >= MapH) {
					ccy = MapH-1;
					cby = CHUNK_HEIGHT-1;
					break;
				}
			}
		}
	}

	Block * GetBlock(int const & a_bX,int const & a_bY);

	void AddBlock(int const & a_X,int const & a_Y,Block * a_B);
	void AddBlock(int const & a_X,int const & a_Y,unsigned int const & a_Data);
	void AddBlockMulti(int const & a_X,int const & a_Y,unsigned int const & a_Data,int Amount);

	void AddBlockMultiChunk(int const & a_bX,int const & a_bY,int const & a_cX,int const & a_cY,unsigned int const & a_Data,int Amount);

	void FillChunk(int const & ChunkX,int const & ChunkY);
	void ClearChunk(int const & ChunkX,int const & ChunkY);

	bool FindNextMatch(Block * a_B);
	bool FindNextMatch(unsigned int const & a_Dat);

	bool FindMatch(int const & a_X,int const & a_Y,Block * a_B);
	bool FindMatch(int const & a_X,int const & a_Y,unsigned int const & a_Dat);

	bool FindMatchInCol(int const & a_X,int const & a_Y,unsigned int const & a_Dat);

	bool FindSolidInCol(int const & a_X,int const & a_Y);

	int FindOpenGroundHeight(int const & x,bool Save);

	void FindNearestOpenGround(int const & a_bX,int const & a_cX,int const & a_cY);

	void SafeStep(Vector2D & C,int const & x,int const & y);

	void LevelGround(int CurHeight);

	void HandleMessage(int const & Msg,void * MsgDat);

	void SetOuterLayer(Chunk_Border *);

	void ApplyTopLayer(int TypeToReplace,int TypeToApply,bool & ToolTrigger,int Max,int Min);

	void ApplyLayer(int TypeToReplace,int TypeToApply,int Depth,bool & ToolTrigger,int Max,int Min);

	//TODO BY : DEFINE
	void PopulateGround();

	void PlaceWinBlock();

	void Mesh();

	void CompleteBorders();

	void Generate() {
		current = &(*pMap)[0];
		current->Load(Buffer);
		for(ccx = 0;ccx < MapW;ccx++) {
			CurrentBiome = BiomeList.getByIndex((*pMap)[ccx].GetBiome());
			Mesh();
			for(int i = 0;i < E_TOOL_SIZE;i++) {
				if(ToolList[i] && CurrentBiome->ToolSet[i]) {
					(_Generator::Ins()->*ToolList[i])();
				}
			}
			CompleteBorders();
		}
		PlaceWinBlock();
		LimiterLayer();
	}

	//Tools
	// E_TOOL_RANDOM - 0
	void RandomizeGroundLevel();

	// E_TOOL_FLAT - 1
	void FlattenGroundData();

	// E_TOOL_CAVE - 2
	void CaveWalk();

	// E_TOOL_GRASS - 10
	void ApplyGrass();

	// E_TOOL_STONE - 11
	void ApplyStone();

	// E_TOOL_TREES - 20
	void PlaceTrees();
	void PlaceLeaves(Vector3D * AllTrees,int const & TreeCount);

	// E_TOOL_TREES - 21
	void PlaceOre1();	

	// Places an un breakable layer around the play area.
	void LimiterLayer();

	//Clean-Up
	void CleanUp();

	//Shut Down, does the final memory wipe
	void ShutDown();
} typedef GENERATOR, * PGENERATOR;